package svgedit.gui.commands;

import java.util.Stack;

import svgedit.gui.Frame;

/**
 * A singleton class that stores the two stacks for adding and redoing. This is
 * the class to reference when adding commands and undo/redo actions
 * 
 * @author SARJ
 * 
 */
public class CommandStack
{

	// Singleton reference
	private static CommandStack instance = null;

	private static Stack<Command> undo = new Stack<Command>();
	private static Stack<Command> redo = new Stack<Command>();

	private boolean doingComposite = false;
	private CompositeCommand compositeCommand;

	/**
	 * Private constructor to enforce singleton
	 */
	private CommandStack()
	{

	}

	/**
	 * Adds a command to the stack,
	 * Automatically handles if composite commands are being added
	 * @param command
	 */
	public void addCommand(Command command)
	{
		if (doingComposite)
		{ // Add it to the currently in progress composite command
			//System.out.println("Adding to composite..");
			compositeCommand.addCommand(command);
		}
		else
		{ // Single command
			undo.push(command);
			command.executeRedo();
			redo.clear();

			// Update the redo/undo menu items - enabled/disabled
			Frame.getInstance().updateCommandMenus();

		}

	}

	/**
	 * Adds a command to the stack, overloaded to support firing or not	 * 
	 * @param command
	 *            The command to add
	 * @param fire
	 *            Whether to fire the command being added
	 */
	public void addCommand(Command command, boolean fire)
	{

		addCommand(command);
		
		// Fire
		if (fire)
			command.executeRedo();
	}

	/**
	 * Begins making a composite command
	 */
	public CompositeCommand startComposite()
	{
		//System.out.println("Composite started");
		doingComposite = true;
		compositeCommand = new CompositeCommand();
		return compositeCommand;
	}

	/**
	 * Begins making a composite command
	 */
	public void endComposite()
	{
		//System.out.println("Composite ended");

		if (compositeCommand.getCommandsCount() != 0)
		{
			undo.push(compositeCommand);
			redo.clear();

		}
		else
			System.err.println("..but not added because it was empty");

		doingComposite = false;

		// Update the redo/undo menu items - enabled/disabled
		Frame.getInstance().updateCommandMenus();

	}

	/**
	 * Called when user performs Undo
	 */
	public void undo()
	{
		if (isUndoable() == false)
			return;

		Command command = undo.pop();
		command.executeUndo(); // Fire the commands action
		redo.push(command);

		// Update the redo/undo menu items - enabled/disabled
		Frame.getInstance().updateCommandMenus();
	}

	/**
	 * Called when user performs Redo
	 */
	public void redo()
	{
		if (isRedoable() == false)
			return;

		Command command = redo.pop();
		command.executeRedo();
		undo.push(command);

		// Update the redo/undo menu items - enabled/disabled
		Frame.getInstance().updateCommandMenus();
	}

	/**
	 * If there are commands on the redo stack
	 * 
	 * @return True/False
	 */
	public boolean isRedoable()
	{
		if (redo.size() > 0)
			return true;
		else
			return false;

	}

	/**
	 * If there are commands on the undo stack
	 * 
	 * @return True/False
	 */
	public boolean isUndoable()
	{
		if (undo.size() > 0)
			return true;
		else
			return false;

	}

	/**
	 * Get the singleton instace
	 * 
	 * @return The singleton instance of the class
	 */
	public static CommandStack getInstance()
	{
		if (instance == null)
			instance = new CommandStack();

		return instance;
	}

	/**
	 * Clear both undo and redo stack
	 * Normally used when opening a new document
	 */
	public void clear()
	{
		undo.clear();
		redo.clear();
		

		// Update the redo/undo menu items - enabled/disabled
		Frame.getInstance().updateCommandMenus();
	}

}
